import sys
import os
import json
import re
from datetime import datetime
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtWebEngineWidgets import *
from PyQt5.QtGui import QIcon, QKeySequence, QDesktopServices


class BrowserTab(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.browser = QWebEngineView()
        self.browser.setUrl(QUrl("https://www.google.com"))

        # 创建该标签页的导航栏
        self.tab_nav_bar = QToolBar()

        back_btn = QAction("←", self)
        back_btn.triggered.connect(self.browser.back)
        self.tab_nav_bar.addAction(back_btn)

        forward_btn = QAction("→", self)
        forward_btn.triggered.connect(self.browser.forward)
        self.tab_nav_bar.addAction(forward_btn)

        reload_btn = QAction("↻", self)
        reload_btn.triggered.connect(self.browser.reload)
        self.tab_nav_bar.addAction(reload_btn)

        layout = QVBoxLayout()
        layout.addWidget(self.tab_nav_bar)
        layout.addWidget(self.browser)
        layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(layout)


class ModernBrowser(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("PyBrowser - 现代化的Python浏览器")
        self.setGeometry(100, 50, 1400, 900)

        # 数据存储
        self.bookmarks = []
        self.history = []
        self.downloads = []
        self.load_data()

        # 搜索引擎配置
        self.search_engines = {
            'google': 'https://www.google.com/search?q=',
            'baidu': 'https://www.baidu.com/s?wd=',
            'bing': 'https://www.bing.com/search?q=',
            'duckduckgo': 'https://duckduckgo.com/?q='
        }
        self.current_search_engine = 'google'

        # 创建主界面
        self.setup_ui()

        # 连接信号
        self.connect_signals()

    def setup_ui(self):
        # 创建标签页组件
        self.tabs = QTabWidget()
        self.tabs.setTabsClosable(True)
        self.tabs.tabCloseRequested.connect(self.close_tab)
        self.tabs.currentChanged.connect(self.tab_changed)

        # 创建主工具栏
        self.create_toolbar()

        # 创建菜单栏
        self.create_menubar()

        # 创建状态栏
        self.status_bar = QStatusBar()
        self.setStatusBar(self.status_bar)

        # 添加第一个标签页
        self.add_new_tab()

        self.setCentralWidget(self.tabs)

    def create_toolbar(self):
        toolbar = QToolBar("主工具栏")
        self.addToolBar(toolbar)

        # 新建标签页按钮
        new_tab_btn = QAction("➕", self)
        new_tab_btn.setToolTip("新建标签页 (Ctrl+T)")
        new_tab_btn.triggered.connect(self.add_new_tab)
        toolbar.addAction(new_tab_btn)

        toolbar.addSeparator()

        # 后退按钮
        back_btn = QAction("←", self)
        back_btn.setToolTip("后退")
        back_btn.triggered.connect(self.navigate_back)
        toolbar.addAction(back_btn)

        # 前进按钮
        forward_btn = QAction("→", self)
        forward_btn.setToolTip("前进")
        forward_btn.triggered.connect(self.navigate_forward)
        toolbar.addAction(forward_btn)

        # 刷新按钮
        reload_btn = QAction("↻", self)
        reload_btn.setToolTip("刷新")
        reload_btn.triggered.connect(self.reload_page)
        toolbar.addAction(reload_btn)

        # 主页按钮
        home_btn = QAction("🏠", self)
        home_btn.setToolTip("主页")
        home_btn.triggered.connect(self.navigate_home)
        toolbar.addAction(home_btn)

        toolbar.addSeparator()

        # 地址栏
        self.url_bar = QLineEdit()
        self.url_bar.setPlaceholderText("输入网址或搜索内容...")
        self.url_bar.returnPressed.connect(self.navigate_to_url)
        toolbar.addWidget(self.url_bar)

        # 搜索引擎选择
        self.search_combo = QComboBox()
        self.search_combo.addItems(["Google", "百度", "Bing", "DuckDuckGo"])
        self.search_combo.currentTextChanged.connect(self.change_search_engine)
        toolbar.addWidget(self.search_combo)

        # 书签按钮
        bookmark_btn = QAction("⭐", self)
        bookmark_btn.setToolTip("添加到书签")
        bookmark_btn.triggered.connect(self.add_to_bookmarks)
        toolbar.addAction(bookmark_btn)

    def create_menubar(self):
        menubar = self.menuBar()

        # 文件菜单
        file_menu = menubar.addMenu("文件")

        new_tab_action = QAction("新建标签页", self)
        new_tab_action.setShortcut(QKeySequence.New)
        new_tab_action.triggered.connect(self.add_new_tab)
        file_menu.addAction(new_tab_action)

        new_window_action = QAction("新建窗口", self)
        new_window_action.setShortcut("Ctrl+N")
        new_window_action.triggered.connect(self.new_window)
        file_menu.addAction(new_window_action)

        file_menu.addSeparator()

        exit_action = QAction("退出", self)
        exit_action.setShortcut(QKeySequence.Quit)
        exit_action.triggered.connect(self.close)
        file_menu.addAction(exit_action)

        # 书签菜单
        bookmark_menu = menubar.addMenu("书签")

        show_bookmarks_action = QAction("显示书签", self)
        show_bookmarks_action.triggered.connect(self.show_bookmarks)
        bookmark_menu.addAction(show_bookmarks_action)

        bookmark_menu.addSeparator()

        # 历史记录菜单
        history_menu = menubar.addMenu("历史记录")

        show_history_action = QAction("显示历史记录", self)
        show_history_action.triggered.connect(self.show_history)
        history_menu.addAction(show_history_action)

        clear_history_action = QAction("清除历史记录", self)
        clear_history_action.triggered.connect(self.clear_history)
        history_menu.addAction(clear_history_action)

        # 工具菜单
        tools_menu = menubar.addMenu("工具")

        downloads_action = QAction("下载管理", self)
        downloads_action.triggered.connect(self.show_downloads)
        tools_menu.addAction(downloads_action)

        # 搜索引擎菜单
        search_menu = menubar.addMenu("搜索引擎")

        google_action = QAction("Google", self)
        google_action.triggered.connect(lambda: self.set_search_engine('google'))
        search_menu.addAction(google_action)

        baidu_action = QAction("百度", self)
        baidu_action.triggered.connect(lambda: self.set_search_engine('baidu'))
        search_menu.addAction(baidu_action)

        bing_action = QAction("Bing", self)
        bing_action.triggered.connect(lambda: self.set_search_engine('bing'))
        search_menu.addAction(bing_action)

        duckduckgo_action = QAction("DuckDuckGo", self)
        duckduckgo_action.triggered.connect(lambda: self.set_search_engine('duckduckgo'))
        search_menu.addAction(duckduckgo_action)

        # 开发者工具
        dev_tools_action = QAction("开发者工具", self)
        dev_tools_action.setShortcut("F12")
        dev_tools_action.triggered.connect(self.toggle_dev_tools)
        tools_menu.addAction(dev_tools_action)

    def connect_signals(self):
        # 连接下载信号
        profile = QWebEngineProfile.defaultProfile()
        profile.downloadRequested.connect(self.on_download_requested)

    def add_new_tab(self, url=None):
        if url is None:
            url = "https://www.google.com"

        tab = BrowserTab()
        browser = tab.browser

        # 设置用户代理，避免被网站识别为爬虫
        browser.page().profile().setHttpUserAgent(
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
        )

        # 启用 JavaScript 和插件
        settings = browser.settings()
        settings.setAttribute(QWebEngineSettings.JavascriptEnabled, True)
        settings.setAttribute(QWebEngineSettings.JavascriptCanOpenWindows, True)
        settings.setAttribute(QWebEngineSettings.PluginsEnabled, True)
        settings.setAttribute(QWebEngineSettings.LocalStorageEnabled, True)
        settings.setAttribute(QWebEngineSettings.LocalContentCanAccessRemoteUrls, True)
        settings.setAttribute(QWebEngineSettings.AllowRunningInsecureContent, True)

        # 连接标签页内浏览器的信号
        browser.urlChanged.connect(lambda q: self.update_url(q, tab))
        browser.titleChanged.connect(lambda title: self.update_title(title, tab))
        browser.loadProgress.connect(self.update_progress)
        browser.loadFinished.connect(self.page_loaded)

        # 处理新窗口请求（解决搜索引擎跳转问题）
        browser.page().profile().setHttpUserAgent(
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
        )

        # 处理导航请求 - 使用新的API
        browser.page().loadStarted.connect(lambda: self.on_load_started(browser))

        index = self.tabs.addTab(tab, "新标签页")
        self.tabs.setCurrentIndex(index)

        if url:
            browser.setUrl(QUrl(url))

        return tab

    def on_load_started(self, browser):
        """页面开始加载时的处理"""
        # 这里可以添加页面加载前的预处理
        pass

    def handle_external_links(self, url):
        """处理外部链接，特别是搜索引擎的跳转"""
        url_str = url.toString()

        # 如果是搜索引擎的安全跳转链接，直接在新标签页打开目标URL
        if self.is_search_engine_redirect(url_str):
            target_url = self.extract_redirect_url(url_str)
            if target_url:
                self.add_new_tab(target_url)
                return True  # 表示已处理
        return False

    def is_search_engine_redirect(self, url):
        """判断是否为搜索引擎的安全跳转链接"""
        redirect_patterns = [
            r'^https?://www\.google\.com/url\?',
            r'^https?://www\.baidu\.com/link\?',
            r'^https?://www\.bing\.com/ck/?\?',
            r'^https?://r\.baidu\.com/',
            r'^https?://link\.zhihu\.com/\?',
            r'^https?://safe\.tbk\.dingdanxia\.com/safe/'
        ]

        return any(re.match(pattern, url) for pattern in redirect_patterns)

    def extract_redirect_url(self, url):
        """从搜索引擎的安全跳转链接中提取真实URL"""
        try:
            from urllib.parse import urlparse, parse_qs, unquote

            # 先解码URL
            url = unquote(url)
            parsed = urlparse(url)
            query_params = parse_qs(parsed.query)

            # Google
            if 'www.google.com' in url and 'url' in query_params:
                return query_params['url'][0]

            # 百度
            elif 'www.baidu.com' in url and 'url' in query_params:
                return query_params['url'][0]
            elif 'baidu.com' in url and 'link' in url:
                # 百度链接需要进一步处理
                if 'url' in query_params:
                    return query_params['url'][0]

            # Bing
            elif 'bing.com' in url:
                if 'a' in query_params:
                    return query_params['a'][0]
                # 新的Bing重定向模式
                elif 'u' in query_params:
                    return query_params['u'][0]

            # 知乎
            elif 'zhihu.com' in url and 'target' in query_params:
                return query_params['target'][0]

            # 其他情况，直接返回原URL
            return url

        except Exception as e:
            print(f"提取重定向URL失败: {e}")
            return url

    def change_search_engine(self, engine_name):
        """更改搜索引擎"""
        engine_map = {
            "Google": "google",
            "百度": "baidu",
            "Bing": "bing",
            "DuckDuckGo": "duckduckgo"
        }
        if engine_name in engine_map:
            self.current_search_engine = engine_map[engine_name]

    def set_search_engine(self, engine):
        """设置搜索引擎"""
        engine_map = {
            "google": "Google",
            "baidu": "百度",
            "bing": "Bing",
            "duckduckgo": "DuckDuckGo"
        }
        if engine in engine_map:
            self.current_search_engine = engine
            self.search_combo.setCurrentText(engine_map[engine])

    def close_tab(self, index):
        if self.tabs.count() > 1:
            self.tabs.removeTab(index)
        else:
            self.close()

    def tab_changed(self, index):
        if index >= 0:
            current_tab = self.tabs.widget(index)
            if current_tab:
                self.update_url(current_tab.browser.url(), current_tab)

    def navigate_back(self):
        current_tab = self.current_tab()
        if current_tab:
            current_tab.browser.back()

    def navigate_forward(self):
        current_tab = self.current_tab()
        if current_tab:
            current_tab.browser.forward()

    def reload_page(self):
        current_tab = self.current_tab()
        if current_tab:
            current_tab.browser.reload()

    def navigate_home(self):
        current_tab = self.current_tab()
        if current_tab:
            current_tab.browser.setUrl(QUrl("https://www.google.com"))

    def navigate_to_url(self):
        url = self.url_bar.text().strip()
        current_tab = self.current_tab()
        if current_tab:
            if not url:
                return

            # 判断输入的是URL还是搜索关键词
            if self.is_url(url):
                if not url.startswith(('http://', 'https://')):
                    url = 'https://' + url
                # 检查是否是搜索引擎重定向链接
                if self.handle_external_links(QUrl(url)):
                    return
                current_tab.browser.setUrl(QUrl(url))
            else:
                # 搜索关键词
                search_url = self.search_engines[self.current_search_engine] + url
                current_tab.browser.setUrl(QUrl(search_url))

    def is_url(self, text):
        """判断输入文本是否是URL"""
        url_patterns = [
            r'^https?://',
            r'^www\.',
            r'^[a-zA-Z0-9-]+\.[a-zA-Z]{2,}',
            r'^localhost',
            r'^\d+\.\d+\.\d+\.\d+'
        ]

        return any(re.match(pattern, text) for pattern in url_patterns)

    def update_url(self, q, tab=None):
        if tab == self.current_tab() or tab is None:
            url_str = q.toString()
            self.url_bar.setText(url_str)

            # 检查是否是搜索引擎重定向链接，如果是则在新标签页打开
            if self.is_search_engine_redirect(url_str):
                target_url = self.extract_redirect_url(url_str)
                if target_url and target_url != url_str:
                    self.add_new_tab(target_url)
                    # 不将重定向链接添加到历史记录
                    return

            # 添加到历史记录
            self.add_to_history(url_str, self.current_browser().title())

    def update_title(self, title, tab):
        index = self.tabs.indexOf(tab)
        if index != -1:
            # 限制标题长度
            if len(title) > 20:
                title = title[:20] + "..."
            self.tabs.setTabText(index, title)

    def update_progress(self, progress):
        if progress < 100:
            self.status_bar.showMessage(f"加载中... {progress}%")
        else:
            self.status_bar.clearMessage()

    def page_loaded(self):
        self.status_bar.showMessage("页面加载完成", 2000)

    def current_tab(self):
        return self.tabs.currentWidget()

    def current_browser(self):
        current_tab = self.current_tab()
        return current_tab.browser if current_tab else None

    def add_to_bookmarks(self):
        current_browser = self.current_browser()
        if current_browser:
            url = current_browser.url().toString()
            title = current_browser.title()

            bookmark = {
                'title': title,
                'url': url,
                'date': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            }

            if bookmark not in self.bookmarks:
                self.bookmarks.append(bookmark)
                self.save_data()
                self.status_bar.showMessage(f"已添加书签: {title}", 3000)

    def add_to_history(self, url, title):
        # 不记录搜索引擎的重定向链接
        if self.is_search_engine_redirect(url):
            return

        history_item = {
            'title': title,
            'url': url,
            'date': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        }

        # 避免重复添加相同的历史记录
        if not self.history or self.history[-1]['url'] != url:
            self.history.append(history_item)
            # 限制历史记录数量
            if len(self.history) > 100:
                self.history.pop(0)
            self.save_data()

    def on_download_requested(self, download):
        # 设置下载路径
        download_path = QFileDialog.getSaveFileName(
            self, "保存文件", os.path.expanduser("~/Downloads/" + download.url().fileName())
        )[0]

        if download_path:
            download.setPath(download_path)
            download.accept()

            # 添加到下载列表
            download_item = {
                'filename': os.path.basename(download_path),
                'url': download.url().toString(),
                'path': download_path,
                'date': datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                'status': '下载中'
            }
            self.downloads.append(download_item)

            # 连接下载完成信号
            download.finished.connect(lambda: self.download_finished(download_item))

    def download_finished(self, download_item):
        download_item['status'] = '已完成'
        self.save_data()
        self.status_bar.showMessage(f"下载完成: {download_item['filename']}", 3000)

    def show_bookmarks(self):
        dialog = QDialog(self)
        dialog.setWindowTitle("书签管理")
        dialog.setGeometry(200, 200, 600, 400)

        layout = QVBoxLayout()

        # 书签列表
        list_widget = QListWidget()
        for bookmark in self.bookmarks:
            item = QListWidgetItem(f"{bookmark['title']}\n{bookmark['url']}")
            list_widget.addItem(item)

        layout.addWidget(list_widget)

        # 按钮
        button_layout = QHBoxLayout()

        open_btn = QPushButton("打开")
        open_btn.clicked.connect(lambda: self.open_bookmark(list_widget, dialog))
        button_layout.addWidget(open_btn)

        delete_btn = QPushButton("删除")
        delete_btn.clicked.connect(lambda: self.delete_bookmark(list_widget))
        button_layout.addWidget(delete_btn)

        close_btn = QPushButton("关闭")
        close_btn.clicked.connect(dialog.close)
        button_layout.addWidget(close_btn)

        layout.addLayout(button_layout)
        dialog.setLayout(layout)
        dialog.exec_()

    def open_bookmark(self, list_widget, dialog):
        current_item = list_widget.currentItem()
        if current_item:
            index = list_widget.currentRow()
            url = self.bookmarks[index]['url']
            self.add_new_tab(url)
            dialog.close()

    def delete_bookmark(self, list_widget):
        current_row = list_widget.currentRow()
        if current_row >= 0:
            self.bookmarks.pop(current_row)
            list_widget.takeItem(current_row)
            self.save_data()

    def show_history(self):
        dialog = QDialog(self)
        dialog.setWindowTitle("历史记录")
        dialog.setGeometry(200, 200, 700, 500)

        layout = QVBoxLayout()

        # 历史记录表格
        table = QTableWidget()
        table.setColumnCount(3)
        table.setHorizontalHeaderLabels(["标题", "网址", "访问时间"])

        table.setRowCount(len(self.history))
        for i, item in enumerate(reversed(self.history)):
            table.setItem(i, 0, QTableWidgetItem(item['title']))
            table.setItem(i, 1, QTableWidgetItem(item['url']))
            table.setItem(i, 2, QTableWidgetItem(item['date']))

        table.resizeColumnsToContents()
        layout.addWidget(table)

        # 按钮
        button_layout = QHBoxLayout()

        open_btn = QPushButton("打开")
        open_btn.clicked.connect(lambda: self.open_history_item(table, dialog))
        button_layout.addWidget(open_btn)

        clear_btn = QPushButton("清除历史记录")
        clear_btn.clicked.connect(lambda: self.clear_history_table(table))
        button_layout.addWidget(clear_btn)

        close_btn = QPushButton("关闭")
        close_btn.clicked.connect(dialog.close)
        button_layout.addWidget(close_btn)

        layout.addLayout(button_layout)
        dialog.setLayout(layout)
        dialog.exec_()

    def open_history_item(self, table, dialog):
        current_row = table.currentRow()
        if current_row >= 0:
            # 历史记录是倒序显示的
            index = len(self.history) - 1 - current_row
            url = self.history[index]['url']
            self.add_new_tab(url)
            dialog.close()

    def clear_history_table(self, table):
        self.history.clear()
        table.setRowCount(0)
        self.save_data()

    def clear_history(self):
        self.history.clear()
        self.save_data()
        self.status_bar.showMessage("历史记录已清除", 3000)

    def show_downloads(self):
        dialog = QDialog(self)
        dialog.setWindowTitle("下载管理")
        dialog.setGeometry(200, 200, 800, 400)

        layout = QVBoxLayout()

        # 下载列表表格
        table = QTableWidget()
        table.setColumnCount(4)
        table.setHorizontalHeaderLabels(["文件名", "保存路径", "下载时间", "状态"])

        table.setRowCount(len(self.downloads))
        for i, download in enumerate(self.downloads):
            table.setItem(i, 0, QTableWidgetItem(download['filename']))
            table.setItem(i, 1, QTableWidgetItem(download['path']))
            table.setItem(i, 2, QTableWidgetItem(download['date']))
            table.setItem(i, 3, QTableWidgetItem(download['status']))

        table.resizeColumnsToContents()
        layout.addWidget(table)

        # 按钮
        button_layout = QHBoxLayout()

        open_btn = QPushButton("打开文件位置")
        open_btn.clicked.connect(lambda: self.open_download_location(table))
        button_layout.addWidget(open_btn)

        clear_btn = QPushButton("清除已完成")
        clear_btn.clicked.connect(lambda: self.clear_completed_downloads(table))
        button_layout.addWidget(clear_btn)

        close_btn = QPushButton("关闭")
        close_btn.clicked.connect(dialog.close)
        button_layout.addWidget(close_btn)

        layout.addLayout(button_layout)
        dialog.setLayout(layout)
        dialog.exec_()

    def open_download_location(self, table):
        current_row = table.currentRow()
        if current_row >= 0 and current_row < len(self.downloads):
            path = self.downloads[current_row]['path']
            directory = os.path.dirname(path)
            QDesktopServices.openUrl(QUrl.fromLocalFile(directory))

    def clear_completed_downloads(self, table):
        self.downloads = [d for d in self.downloads if d['status'] != '已完成']
        table.setRowCount(len(self.downloads))
        for i, download in enumerate(self.downloads):
            table.setItem(i, 0, QTableWidgetItem(download['filename']))
            table.setItem(i, 1, QTableWidgetItem(download['path']))
            table.setItem(i, 2, QTableWidgetItem(download['date']))
            table.setItem(i, 3, QTableWidgetItem(download['status']))
        self.save_data()

    def toggle_dev_tools(self):
        current_browser = self.current_browser()
        if current_browser:
            current_browser.triggerPageAction(QWebEnginePage.InspectElement)

    def new_window(self):
        new_browser = ModernBrowser()
        new_browser.show()

    def load_data(self):
        try:
            if os.path.exists('browser_data.json'):
                with open('browser_data.json', 'r', encoding='utf-8') as f:
                    data = json.load(f)
                    self.bookmarks = data.get('bookmarks', [])
                    self.history = data.get('history', [])
                    self.downloads = data.get('downloads', [])
        except Exception as e:
            print(f"加载数据失败: {e}")

    def save_data(self):
        try:
            data = {
                'bookmarks': self.bookmarks,
                'history': self.history,
                'downloads': self.downloads
            }
            with open('browser_data.json', 'w', encoding='utf-8') as f:
                json.dump(data, f, ensure_ascii=False, indent=2)
        except Exception as e:
            print(f"保存数据失败: {e}")

    def closeEvent(self, event):
        self.save_data()
        event.accept()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    app.setApplicationName("PyBrowser")

    # 设置样式
    app.setStyle('Fusion')

    browser = ModernBrowser()
    browser.show()

    sys.exit(app.exec_())